package com.itheima.mp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.AddressVO;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.enums.UserStatus;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    /**
     * 扣除用户金额
     * @param id
     * @param money
     */
    @Override
    @Transactional
    public void deductBalanceById(Long id, Integer money) {
        // 根据id获取用户信息  getById就是当前UserService继承至IService中的方法，
        // 因为是在自己的实现类中调用的，不需要注入该对象，直接使用即可
        User user = getById(id);
        // 判断用户状态是否正确
        if(user==null || user.getStatus().equals(UserStatus.FREZEE)){
            throw new RuntimeException("用户状态有误");
        }
        // 判断金额是否足够
        if(user.getBalance()-money < 0){
            throw new RuntimeException("用户金额不足");
        }
        // 扣除用户金额 第一种写法
        // baseMapper.deductBalanceById(id, money);
        // 第二种写法
        Integer remainBalance = user.getBalance()-money;
        lambdaUpdate()
                .set(User::getBalance, remainBalance)
                .set(remainBalance==0, User::getStatus, UserStatus.FREZEE)
                .eq(id!=null,User::getId, id)
                .eq(User::getBalance, user.getBalance()) // 乐观锁  用户的余额等于我刚刚查到的用户余额
                .update();
    }

    /**
     * 根据id查询用户 使用Db静态工具
     * @param id
     * @return
     */
    @Override
    public UserVO queryUserAndAddressById(Long id) {
        // 获取用户信息
        User user = getById(id);
        if(user==null || user.getStatus()==UserStatus.FREZEE){
            throw new RuntimeException("用户状态异常");
        }
        // 获取用户对应的地址列表
        List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();
        // 将数据类型转换为VO
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        if(CollUtil.isNotEmpty(addresses)){
            userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
        }
        return userVO;
    }

    /**
     * 根据id集合批量查询用户 使用Db静态工具
     * @param ids
     * @return
     */
    @Override
    public List<UserVO> batchQueryUsersByIds(List<Long> ids) {
        // 1 根据id集合查询用户列表
        List<User> users = listByIds(ids);
        if(CollUtil.isEmpty(users)){
            throw new RuntimeException("用户列表为空");
        }
        // 2.1 根据用户列表获取用户id集合
        List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        // 2.2 根据用户id集合获取用户地址集合(所有要查询的用户地址集合，不仅仅是单独的一个用户的)
        List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
        // 2.3 将地址Address集合转换为AddressVO
        List<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class);
        // 2.4 通过stram流的分组功能，将对应的用户地址拆分到map集合中，键名是用户id，键值是对应的用户地址
        Map<Long, List<AddressVO>> userAndAddress = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
        // 3 循环用户列表
        // 新建一个集合存放用户数据，给一个用户长度这样就不会在动态的去扩容集合空间
        List<UserVO> userVOS = new ArrayList<>(users.size());
        // 3.1 将User对象转换为UserVO对象
        for (User user : users) {
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            userVO.setAddresses(userAndAddress.get(user.getId()));
            userVOS.add(userVO);
        }
        // 3.2 根据用户id从map集合中取出用户的地址列表，给当前用户UserVO对象
        return userVOS;
    }
}
